/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2020年9月4日
 */
package com.massyframework.beanskin.runtime.service;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Objects;
import java.util.function.Predicate;

import com.massyframework.beanskin.Module;
import com.massyframework.beanskin.service.ServiceFactory;
import com.massyframework.beanskin.service.ServicePropertiesBuilder;
import com.massyframework.beanskin.service.ServiceReference;
import com.massyframework.beanskin.service.ServiceRegistration;
import com.massyframework.beanskin.util.classloading.ModuleClassLoader;

/**
 * {@link AbstractServiceRegistration} 提供 {@link ServiceRegistration}的基本语义
 * @param <S>
 */
abstract class AbstractServiceRegistration<S> implements ServiceRegistration<S> {

    private final Object service;
    private final boolean isFactory;
    private final ServiceReference<S> reference;
    private final Predicate<String> allowAccess;

    /**
     * 构造方法
     * @param service {@link Object}, 服务实例
     * @param builder {@link ServicePropertiesBuilder}, 服务属性构建器
     */
    public AbstractServiceRegistration(Object service, ServicePropertiesBuilder builder){
        this.reference = new DefaultServiceReference<S>(builder.build());
        this.service = Objects.requireNonNull(service, "\"service\" cannot be null.");
        this.isFactory = ServiceFactory.class.isAssignableFrom(service.getClass());
        this.allowAccess = AllowAccessPredicateFactory.createAllowAccess(this.reference.getAllowAccessGroupId());
        
        this.checkService(service,  this.reference.getExportTypes());
    }

    /**
     * 服务引用
     *
     * @return {@link ServiceReference}
     */
    @Override
    public ServiceReference<S> getServiceReference() {
        return this.reference;
    }
    
    /**
     * 获取服务实例
     *
     * @param assembly {@link Module}, 请求服务实例的模块
     * @return {@link S}
     */
    @SuppressWarnings("unchecked")
	@Override
    public S getService(Module<?> module) {
        return this.isFactory ?
                ((ServiceFactory<S>) service).getService(module) :
                (S) this.service;
    }

    /**
     * 是否允许访问的断言
     * @return {@link Predicate}
     */
    public Predicate<String> getAllowAccess(){
        return this.allowAccess;
    }

    /**
     * 检查服务实例是否和输出服务匹配
     * @param service {@link Object}
     * @param exportTypes {@link Class}数组，输出服务
     */
    protected void checkService(Object service, Class<?>[] exportTypes) {
    	if (!isFactory) {
    		Class<?> serviceType = service.getClass();
    		this.checkExportTypes(exportTypes, serviceType);
    	}else {
    		Class<?> serviceType = this.getServiceTypeFromServiceFactory(service.getClass());
    		this.checkExportTypes(exportTypes, serviceType);
    	}
    }
    
    /**
     * 获取服务类型
     * @param service
     * @return
     */
    protected Class<?> getServiceTypeFromServiceFactory(Class<?> clazz){
    	if (clazz == Object.class) return null;
    	
    	Type[] types = clazz.getGenericInterfaces();
    	for (Type type: types) {
    		if (type instanceof ParameterizedType) {
    			ParameterizedType paramType = (ParameterizedType)type;
    			if (paramType.getRawType()== ServiceFactory.class) {
    				return (Class<?>)paramType.getActualTypeArguments()[0];
    			}
    		}
    	}
    	
    	return getServiceTypeFromServiceFactory(clazz.getSuperclass());
    }
    
    /**
     * 检查输出服务
     * @param exportTypes {@link Class}数组，输出的服务
     * @param serviceType {@link Class},实际服务的类型
     */
    protected void checkExportTypes(Class<?>[] exportTypes, Class<?> serviceType) {
    	for (Class<?> exportType: exportTypes) {
			if (!exportType.isAssignableFrom(serviceType)) {
				if (this.isFactory &&  serviceType.isInterface()) {
					if (!serviceType.isAssignableFrom(exportType)) {
						throw new IllegalArgumentException("service[" + service.getClass() +
								"] not implements exprotType[" + exportType + "].");
					}
				}else {
					throw new IllegalArgumentException("service[" + service.getClass() +
						"] not implements exprotType[" + exportType + "].");
				}
			}
		}
    }
    
    protected boolean isMatch(Module<?> module, ModuleClassLoader classLoader) {
    	Class<?>[] types = this.reference.getExportTypes();
    	for (Class<?> type: types) {
    		if (type.getClassLoader() == classLoader) {
    			return true;
    		}
    	}
    	
    	if (this.service.getClass().getClassLoader() == classLoader) {
    		return true;
    	}
    	
    	if (module.getGroupId().equals(this.reference.getGroupId())
    			&& module.getModuleId().equals(this.reference.getModuleId())
    			&& module.getVersion().equals(this.reference.getModuleVersion())) {
    		return true;
    	}
    	    	
    	return false;
    }
    
    
    
    @Override
	public final boolean unregister() {
		return this.unregister(false);
	}

	/**
     * 是否强制取消注册
     * @param force {@link boolean},true表示强制
     * @return {@link boolean},返回是否注销成功.
     */
    protected abstract boolean unregister(boolean force);
}

